package com.thenie.seckill.util;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Component;

import java.time.Duration;
import java.util.Collections;
import java.util.concurrent.TimeUnit;

/**
 * @Description 分布式锁
 * @Author thenie
 * @Data 2022/5/6 21:12
 */
@Component
public class RedisLockUtils {
    @Autowired
    private RedisTemplate redisTemplate;

    //分布式锁过期时间 s  可以根据业务自己调节
    private static final Long LOCK_REDIS_TIMEOUT = 5L;
    //分布式锁休眠 至 再次尝试获取 的等待时间 ms 可以根据业务自己调节
    public static final Long LOCK_REDIS_WAIT = 500L;


    /**
     *  加锁
     **/
    public Boolean getLock(String key, int value){
        Boolean lockStatus = this.redisTemplate.opsForValue().setIfAbsent(key,value, Duration.ofSeconds(LOCK_REDIS_TIMEOUT));
        return lockStatus;
    }

    /**
     *  释放锁
     **/
    public Long releaseLock(String key, int value){
        String luaScript = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        RedisScript<Long> redisScript = new DefaultRedisScript<>(luaScript,Long.class);
        Long releaseStatus = (Long)this.redisTemplate.execute(redisScript, Collections.singletonList(key),value);
        return releaseStatus;
    }

    /**
     * 实现原子性递减操作
     * @param key
     * @return
     */
    public Long executeDecrToZero(String key){
        String luaScript=
                "if (redis.call('exists', KEYS[1]) == 1) then\n" +
                    "local stock = tonumber(redis.call('get', KEYS[1]));\n" +
                    "if (stock > 0) then\n" +
                        "redis.call('incrby', KEYS[1], -1);\n" +
                        "return stock;\n" +
                    "end;\n" +
                "return 0;\n" +
                "end;";
        RedisScript<Long> redisScript = new DefaultRedisScript<>(luaScript,Long.class);
        return (Long) redisTemplate.execute(redisScript,Collections.singletonList(key),Collections.EMPTY_LIST);
    }

    /**
     * 请求次数最大限制
     * @param uri
     * @param upperLimit
     * @return
     */
    public boolean isRequestUpperLimit(String uri, long upperLimit){
        if (redisTemplate.hasKey(uri)){
            Long count = redisTemplate.opsForValue().increment(uri);
            if (count > upperLimit){
                return true;
            }
        }else{
            redisTemplate.opsForValue().set(uri,1,60, TimeUnit.SECONDS);
        }
        return false;
    }
}
